package by.bsu.diploma.ga;

import by.bsu.diploma.common.Grid;
import by.bsu.diploma.system.Configuration;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author Anatoly Selitsky
 */
public class GeneticAlgorithm {

    public static final int POPULATION_SIZE = 100;
    public static final int MAX_ITERATION = 100;
    public static final double SURVIVE_RATE = 0.5;
    public static final double MUTATE_RATE = 0.3;
    public static final double ELITE_RATE = 0.5;

    private Configuration config;

    public GeneticAlgorithm(Configuration configuration) {
        this.config = configuration;
    }

    public void initPopulation(List<Genome> population) {
        for (int i = 0; i < POPULATION_SIZE; i++) {
            GenomeGenerator generator = new GenomeGenerator(config);
            population.add(generator.generate());
        }
    }

    public void choiceElite(List<Genome> population, List<Genome> children, int eliteSize) {
        for (int i = 0; i < eliteSize; i++) {
            children.add(population.get(i));
        }
    }

    public Genome[] doCrossover(Genome parent1, Genome parent2, int row, int col) {
        int[][] grid1 = parent1.getGrid().getCells(),
                grid2 = parent2.getGrid().getCells();
        int[][] offspring1 = copySubArray(grid1, grid2, row, col);
        int[][] offspring2 = copySubArray(grid2, grid1, row, col);
        return new Genome[] {new Genome(new Grid(offspring1)), new Genome(new Grid(offspring2))};
    }

    public void mutate(Genome genome) {
        // todo
    }

    public List<Genome> mutate(List<Genome> population) {
        int eliteSize = (int) (ELITE_RATE * POPULATION_SIZE);
        List<Genome> children = new ArrayList<Genome>();
        choiceElite(population, children, eliteSize);
        for (int i = eliteSize; i < POPULATION_SIZE; i++) {
            int pi1 = (int) (Math.random() * SURVIVE_RATE * POPULATION_SIZE);
            int pi2 = (int) (Math.random() * SURVIVE_RATE * POPULATION_SIZE);
            int row = (int) (Math.random() * config.getRowCount());
            int col = (int) (Math.random() * config.getColCount());
            Genome[] genomes = doCrossover(population.get(pi1), population.get(pi2), row, col);
            children.add(i, genomes[0]);
            children.add(i + 1, genomes[1]);
            if (Math.random() < MUTATE_RATE) {
                mutate(children.get(i));
                mutate(children.get(i + 1));
            }
            i++;
        }
        return children;
    }

    public void run() {
        List<Genome> population = new ArrayList<Genome>();
        initPopulation(population);
        for (Genome genome : population) {
            System.out.println(genome);
        }
        System.out.println(" ------------------------ ");
        for (int i = 0; i < MAX_ITERATION; i++) {
            Collections.sort(population);
            Genome genome = population.get(0);
            System.out.println("i = " + i + ", " + genome);
            if (genome.getFitness() == 0) {
                break;
            }
            population = mutate(population);
        }
        System.out.println(" -------- BEST --------- ");
        System.out.println(population.get(0));
    }

    private int[][] copySubArray(int[][] a, int[][] b, int row, int col) {
        int offspring[][] = new int[config.getRowCount()][config.getColCount()];
        for (int i = 0 ; i < config.getRowCount(); i++) {
            for(int j = 0; j < config.getColCount(); j++) {
                if (i >= row && j >= col) {
                    offspring[i][j] = b[i][j];
                } else {
                    offspring[i][j] = a[i][j];
                }
            }
        }
        return offspring;
    }

}
